#pragma once

#include <iostream>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string>
#include <strings.h>
#include <cerrno>
#include <unistd.h>
#include <pthread.h>
#include <cstring>
#include "nocopy.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include "LockGuard.hpp"
#include <functional>
#include "Comm.hpp"
#include "ThreadPool.hpp"

using namespace std;

using task_t = function<void()>;
// using cb_t = function<string (string)>; // 定义函数类型

const static uint16_t defaultport = 8888;
const static int defaultfd = -1;
const static int defaultsize = 1024;

class UdpServer:public nocopy
{
public:
    // UdpServer(cb_t OnMessage, uint16_t port)
    //     :_port(port)
    //     ,_sockfd(defaultfd)
    //     ,_OnMessage(OnMessage)
    // {}

    UdpServer(uint16_t port)
        :_port(port)
        ,_sockfd(defaultfd)
    {
        pthread_mutex_init(&_mutex, nullptr);
    }

    void Init()
    {
        // 创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd < 0)
        {
            lg.LogMessage(Fatal, "socket err, %d : %s", errno, strerror(errno));
            exit(Socket_Err);
        }
        lg.LogMessage(Info, "socket success, sockfd: %d\n", _sockfd);

        // 绑定，指定网络信息
        struct sockaddr_in local;
        bzero(&local, sizeof(local)); // 初始化
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        // local.sin_addr.s_addr = inet_addr(_ip.c_str());

        // 结构填写完整，转到内核
        int n = ::bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
        if(n != 0)
        {
            lg.LogMessage(Fatal, "socket err, %d : %s", errno, strerror(errno));
            exit(Bind_Err);
        }

        ThreadPool<task_t>::GetInstance()->Start();
    }

    void AddOnlineUser(InetAddr addr)
    {
        LockGuard lockguard(&_mutex);
        for(auto &user : _online_user)
        {
            if(user == addr) break;
        }
        _online_user.push_back(addr);
        lg.LogMessage(Debug, "%s:%d is add to onlineuser list ... \n", addr.Ip().c_str(), addr.Port());
    }

    void Route(int sockfd, string &message)
    {
        LockGuard lockguard(&_mutex);
        for(auto &user : _online_user)
        {
            sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&user.GetAddr(), sizeof(user.GetAddr()));
            lg.LogMessage(Debug, "server send message to %s:%d, message: %s\n", user.Ip().c_str(), user.Port(), message.c_str());
        }
    }

    void Start()
    {
        // 服务器一直运行
        char buffer[defaultsize];
        for(;;)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer); // 不能乱写
            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
            if(n > 0)
            {
                // uint16_t clientport = ntohs(peer.sin_port);
                // string clientip = inet_ntoa(peer.sin_addr);
                // string clientinfo = clientip;
                // clientinfo += ":";
                // clientinfo += to_string(clientport); // "127.0.0.1:8888"
                InetAddr addr(peer);
                AddOnlineUser(addr);
                buffer[n] = 0;
                
                string message = "["; 
                message += addr.Ip();
                message += ":";
                message += to_string(addr.Port());
                message += "]# ";
                message += buffer; 
                task_t task = std::bind(&UdpServer::Route, this, _sockfd, message);
                ThreadPool<task_t>::GetInstance()->Push(task);

                // string response = _OnMessage(buffer);
                // cout << "[" << addr.PrintDebug() << "]# " << response.c_str() << endl;
                // sendto(_sockfd, response.c_str(), response.size(), 0, (struct sockaddr*)&peer, len);
            }
        }
    }

    ~UdpServer()
    {
        pthread_mutex_destroy(&_mutex);
    }

private:
    // string _ip;
    uint16_t _port;
    int _sockfd;
    // cb_t _OnMessage; // 回调

    pthread_mutex_t _mutex;
    vector<InetAddr> _online_user; // 被多个线程同时访问
};